#pragma once
#include "ModelFormation.h"
#include <QVector>
#include <QHash>
#include <QSet>
#include <cmath>
#include <algorithm>
#include <execution>
#include <functional>
#include <QDebug>

namespace ContourProcessing {

using Point3D = QVector3D;
using ColorRGBA = QVector4D;
using Slice = QVector<QVector<Vertex>>;
using ContourData = QVector<Slice>;

using Gray16 = quint16;
constexpr Gray16 GRAY16_MAX = 65535;
constexpr Gray16 DEFAULT_BINARY_THRESHOLD = 5000;

enum class InterpolationMode {
    Gray,
    VChannel,
    Hybrid
};

struct VertexConversionResult {
    QVector<Vertex> vertices;
    QVector<int> sliceStartIndices;
    QVector<int> contourStartIndices;
    int totalSlices = 0;
    int totalContours = 0;
};

struct GlobalStats {
    int totalSlices = 0;
    int totalContours = 0;
    int totalPoints = 0;
    Gray16 maxGray = 0;
    Gray16 maxV = 0;
    Gray16 maxIntensity = 0;
    float maxDistance = 0.0f;
    float avgDistance = 0.0f;
    float stdDistance = 0.0f;
    int distanceCount = 0;
    InterpolationMode mode = InterpolationMode::Gray;
};

struct ProcessingParams {
    Gray16 binaryThreshold = DEFAULT_BINARY_THRESHOLD;
    bool binaryInvert = false;
    float lowerRemovalRatio = 0.85f;
    int higherBaseCount = 1;
    float higherDistanceWeight = 0.3f;
    float higherIntensityWeight = 0.7f;
    InterpolationMode interpMode = InterpolationMode::Hybrid;
};

struct ProcessingStats {
    int totalOriginalPoints = 0;
    int totalProcessedPoints = 0;
    int totalLowerRemoved = 0;
    int totalHigherAdded = 0;
    int originalLowerPoints = 0;
    int originalHigherPoints = 0;
};

class ContourProcessor {
public:
    ContourProcessor() = default;

    ContourData processContours(const ContourData& inputData,
                                const ProcessingParams& params = ProcessingParams());

    GlobalStats getGlobalStats() const { return m_globalStats; }
    ProcessingStats getProcessingStats() const { return m_processingStats; }

    static VertexConversionResult convertToFlatVertices(const ContourData& processedData);
    static QVector<QVector<QVector<QPointF>>> convertToStacksFormation(const ContourData& processedData, const QVector3D& u, const QVector3D& v);
    static GlobalStats calculateGlobalStats(const ContourData& contoursData,
                                            InterpolationMode mode = InterpolationMode::Gray);

private:
    Gray16 rgbaToGray16(const ColorRGBA& color) const;
    Gray16 rgbaToHSV_V_16bit(const ColorRGBA& color) const;
    Gray16 rgbaToHybridIntensity(const ColorRGBA& color) const;

    template<InterpolationMode Mode>
    Gray16 calculateIntensity(const ColorRGBA& color) const;
    Gray16 gray16ToBinary(Gray16 grayValue, Gray16 threshold, bool invert) const;
    int calculateInterpolationCount(float distance, Gray16 intensityValue,
                                    float maxDistance, Gray16 maxIntensity,
                                    int baseCount, float distanceWeight, float grayWeight) const;
    QVector<std::pair<Point3D, Gray16>> insertPointsForHighIntensity(
        const QVector<Point3D>& points, const QVector<Gray16>& binaryColors,
        const QVector<Gray16>& intensityValues, const GlobalStats& stats,
        const ProcessingParams& params) const;
    QVector<std::pair<Point3D, Gray16>> removeLowIntensityPoints(
        const QVector<Point3D>& points, const QVector<Gray16>& binaryColors,
        float removalRatio) const;
    QVector<Vertex> processSingleContour(const QVector<Vertex>& contour,
                                         const GlobalStats& stats,
                                         const ProcessingParams& params,
                                         ProcessingStats& sliceStats);
    GlobalStats m_globalStats;
    ProcessingStats m_processingStats;
};

class MathUtils {
public:
    static float distance3D(const Point3D& p1, const Point3D& p2);
    static Point3D interpolatePoints(const Point3D& p1, const Point3D& p2, float t);
    template<typename T>
    static T normalize(T value, T maxValue) {
        if (maxValue == 0) return 0;
        return std::min(static_cast<T>(value / maxValue), static_cast<T>(1.0));
    }
    static float power(float base, float exponent) {
        return std::pow(base, exponent);
    }
};

class ColorUtils {
public:
    static Gray16 rgbaToGray16(const ColorRGBA& color);
    static Gray16 rgbaToHSV_V_16bit(const ColorRGBA& color);
    static ColorRGBA binaryToRGBA(Gray16 binaryValue);

private:
    static void rgbToHSV(float r, float g, float b, float& h, float& s, float& v);
};

} // namespace ContourProcessing
